﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using DAP.Adorners;
using System.Text.RegularExpressions;
using System.Windows.Threading;

namespace FarpacHelper
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class EditToolWindow : Window
    {
        CroppingAdorner g_cropadorner;
        FrameworkElement g_feCurrent = null;
        Brush g_brOriginal;
        Tool g_toolCurrent;
        List<Tool> g_toolList;
        int index = 0;
        #region Non-Dispose Variable
        double length = 0;
        double weight = 0;
        double diameter = 0;
        double volt = 0;
        double amp = 0;

        const double CMperFT = 30.48;
        const string CM2FT = "cm --> ft";
        const string FT2CM = "ft --> cm";
        const double LBperKG = 2.20462262;
        const string KG2LBS = "kg --> lbs";
        const string LBS2KG = "lbs --> kg";
        const double CMperIN = 2.54;
        const string CM2IN = "cm --> in";
        const string IN2CM = "in --> cm";
        #endregion

        public EditToolWindow()
        {
            InitializeComponent();
            ToolCollection tstr = new ToolCollection();
            g_toolList = new List<Tool>();
            g_toolList.AddRange(tstr.ToolsCollection);
            g_toolCurrent = g_toolList[index];

        }

        public EditToolWindow(IEnumerable<Tool> tool)
        {
            InitializeComponent();

            g_toolList = new List<Tool>();
            g_toolList.AddRange(tool);
            lbLength.Content = CM2FT; lbWeight.Content = KG2LBS; lbDiameter.Content = CM2IN;
            g_toolCurrent = g_toolList[index];
            imgTool.Source = new BitmapImage(g_toolCurrent.ImageLocation); // There is no way to wait until imgTool finish rendered by Render Thread better way than use Dispatcher.Invoke()
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateToolToUI();
        }

        private void AddCropToElement(FrameworkElement fel, double Xbegin = 0, double Xend = 1)
        {
            if (g_feCurrent != null)
            {
                RemoveCropFromCur();
            }
            Rect rcInterior = new Rect(fel.ActualWidth * Xbegin, 0, fel.ActualWidth * (Xend - Xbegin), fel.ActualHeight);
            //Rect rcInterior = new Rect(0, 0, fel.ActualWidth, fel.ActualHeight);
            AdornerLayer aly = AdornerLayer.GetAdornerLayer(fel);
            g_cropadorner = new CroppingAdorner(fel, rcInterior);
            aly.Add(g_cropadorner);
            imgCrop.Source = g_cropadorner.BpsCrop();
            g_cropadorner.CropChanged += CropChanged;
            g_feCurrent = fel;
            if (rbRed.IsChecked != true)
            {
                SetClipColorGrey();
            }
        }

        private void RemoveCropFromCur()
        {
            AdornerLayer aly = AdornerLayer.GetAdornerLayer(g_feCurrent);
            aly.Remove(g_cropadorner);
        }

        private void RefreshCropImage()
        {
            if (g_cropadorner != null)
            {
                Rect rc = g_cropadorner.ClippingRectangle;

                tblkClippingRectangle.Text = string.Format(
                    "Clipping Rectangle: ({0:N1}, {1:N1}, {2:N1}, {3:N1})",
                    rc.Left,
                    rc.Top,
                    rc.Right,
                    rc.Bottom);
                imgCrop.Source = g_cropadorner.BpsCrop();
            }
        }

        private void CropChanged(Object sender, RoutedEventArgs rea)
        {
            RefreshCropImage();
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //RefreshCropImage();
        }

        private void SetClipColorRed()
        {
            if (g_cropadorner != null)
            {
                g_cropadorner.Fill = g_brOriginal;
            }
        }

        private void SetClipColorGrey()
        {
            if (g_cropadorner != null)
            {
                Color clr = Colors.Black;
                clr.A = 110;
                g_cropadorner.Fill = new SolidColorBrush(clr);
            }
        }

        private void Red_Checked(object sender, RoutedEventArgs e)
        {
            SetClipColorRed();
        }

        private void Grey_Checked(object sender, RoutedEventArgs e)
        {
            SetClipColorGrey();
        }

        private void UpdateToolFromUI()
        {
            try
            {
                // Truoc mat su dung SIZE cai da. Chuyen qua dung MODEL sau
                g_toolCurrent.Size = (cbModel.SelectedItem as ComboBoxItem).Content as string;
                g_toolCurrent.Description = txtDescription.Text;
                g_toolCurrent.Length = length.ToString() + (lbLength.Content == CM2FT ? "cm" : "ft");
                g_toolCurrent.Weight = weight.ToString() + (lbWeight.Content == KG2LBS ? "kg" : "lbs");
                g_toolCurrent.Diameter = diameter.ToString() + (lbDiameter.Content == CM2IN ? "cm" : "in");
                g_toolCurrent.Voltage = volt;
                g_toolCurrent.Current = amp;

                if (g_cropadorner != null)
                {
                    Rect rc = g_cropadorner.ClippingRectangle;
                    g_toolCurrent.Xbegin = rc.Left / imgTool.ActualWidth;
                    g_toolCurrent.Xend = rc.Right / imgTool.ActualWidth;
                }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message, "UpdateToolFromUI: FAIL!"); }
        }

        private void UpdateToolToUI()
        {
            // Consider use : Natural Render before handling any task http://stackoverflow.com/questions/2990373/wait-for-animation-render-to-complete-xaml-and-c-sharp
            imgTool.Source = new BitmapImage(g_toolCurrent.ImageLocation); // There is no way to wait until imgTool finish rendered by Render Thread better way than use Dispatcher.Invoke()

            try  // Truoc mat su dung SIZE cai da. Chuyen qua dung MODEL sau
            {
                cbModel.SelectedIndex = 0;
                string model = GetDoubleFromString(g_toolCurrent.Size).ToString("0.0");
                for (int i = 1; i < cbModel.Items.Count; i++) // Note: 0 for "N/A" no check here
                {
                    string s = GetDoubleFromString((cbModel.Items[i] as ComboBoxItem).Content as string).ToString("0.0");
                    if (s == model) cbModel.SelectedIndex = i;
                }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message, "UpdateToolToUI Fail at cbbModel"); }

            txtDescription.Text = g_toolCurrent.Description;

            #region Get Tool Length
            if (g_toolCurrent.Length.ToLower().Contains("cm"))
            {   // Hien Tool dang o CM -> Check txtLength
                if (lbLength.Content == CM2FT)
                {   // txtLength cung dang CM -> Mup + Update
                    length = g_toolCurrent.LengthCM; UpdateLength();
                }
                else
                {   // txtLength dang o FT -> Mup + Swap
                    length = g_toolCurrent.LengthFT; SwapLength();
                }
            }
            else
            {   // Hien Tool dang o FT hoac cai gi khac -> Check txtLength
                if (lbLength.Content == CM2FT)
                {   // txtLength cung dang FT -> Mup + Update
                    length = g_toolCurrent.LengthFT; UpdateLength();
                }
                else
                {   // txtLength dang o CM -> Mup + Swap
                    length = g_toolCurrent.LengthCM; SwapLength();
                }
            }
            #endregion

            #region Get Tool Weight
            if (g_toolCurrent.Weight.ToLower().Contains("lbs"))
            {   // Hien Tool dang o lbs -> Check txtWeight
                if (lbWeight.Content == LBS2KG)
                {   // txtWeight cung dang lbs -> Mup + Update
                    weight = g_toolCurrent.WeightLB; UpdateWeight();
                }
                else
                {   // txtWeight dang o KG -> Mup + Swap
                    weight = g_toolCurrent.WeightKG; SwapWeight();
                }
            }
            else
            {   // Hien Tool dang o KG hoac cai gi khac -> Check txtWeight
                if (lbWeight.Content == KG2LBS)
                {   // txtWeight cung dang KG -> Mup + Update
                    weight = g_toolCurrent.WeightKG; UpdateWeight();
                }
                else
                {   // txtWeight dang o LBS -> Mup + Swap
                    weight = g_toolCurrent.WeightLB; SwapLength();
                }
            }
            #endregion

            #region Get Tool Diameter
            if (g_toolCurrent.Diameter.ToLower().Contains("cm"))
            {   // Hien Tool dang o CM
                if (lbDiameter.Content == CM2IN) { diameter = g_toolCurrent.DiameterCM; UpdateDiameter(); }
                else { diameter = g_toolCurrent.DiameterIN; SwapDiameter(); }
            }
            else
            {   // Hien Tool dang o IN hoac cai gi khac
                if (lbDiameter.Content == IN2CM) { diameter = g_toolCurrent.DiameterIN; UpdateDiameter(); }
                else { diameter = g_toolCurrent.DiameterCM; SwapDiameter(); }
            }
            #endregion

            volt = g_toolCurrent.Voltage; UpdateVoltage();
            amp = g_toolCurrent.Current; UpdateCurrent();

            // Code to change Crop Rectangle http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/693fbedb-efa6-413e-ab66-530c6961d3fb
            //Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => { })).Wait(); // Khong co dong nay la "rtb.Render(AdornedElement)" se bi tre 1 hinh do UI chua update AdornedElement moi kip
            Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => { }));
            AddCropToElement(imgTool, g_toolCurrent.Xbegin, g_toolCurrent.Xend);
            g_brOriginal = g_cropadorner.Fill;
            RefreshCropImage();
        }

        const double LENGTH_EQUAL_CLEARANCE = 0.01;
        const double WEIGHT_EQUAL_CLEARANCE = 0.05;
        const double DIAMETER_EQUAL_CLEARANCE = 0.001;
        const double VOLT_EQUAL_CLEARANCE = 0.1;
        const double AMP_EQUAL_CLEARANCE = 0.001;
        const double XSCALE_EQUAL_CLEARANCE = 0.0001; // 1/1,000 kích thước hình. Nếu kích thước hình là 1,000pixel thì thay đổi 1 pixel mới công nhận là thay đổi.
        private bool ToolDifferentFromUI()
        {
            // So sanh Model (Truoc mat su dung SIZE)
            string model = GetDoubleFromString(g_toolCurrent.Size).ToString("0.0");
            string s = GetDoubleFromString((cbModel.SelectedItem as ComboBoxItem).Content as string).ToString("0.0");
            if (s != model) return true;

            // Compare Description
            if (txtDescription.Text.Trim().ToLower() != g_toolCurrent.Description.Trim().ToLower()) return true;

            // Compare Tool Length
            if (lbLength.Content == CM2FT)
            {   // UI dang CM -> so sanh voi LengthCM
                if (Math.Abs(length - g_toolCurrent.LengthCM) > LENGTH_EQUAL_CLEARANCE) return true;
            }
            else
            {   // UI dang FT -> so sanh voi LengthFT
                if (Math.Abs(length - g_toolCurrent.LengthFT) > LENGTH_EQUAL_CLEARANCE) return true;
            }


            // Compare Tool Weight
            if (lbWeight.Content == LBS2KG) { if (Math.Abs(weight - g_toolCurrent.WeightLB) > WEIGHT_EQUAL_CLEARANCE) return true; }
            else { if (Math.Abs(weight - g_toolCurrent.WeightKG) > WEIGHT_EQUAL_CLEARANCE) return true; }

            // Compare Tool Diameter
            if (lbDiameter.Content == CM2IN) { if (Math.Abs(diameter - g_toolCurrent.DiameterCM) > DIAMETER_EQUAL_CLEARANCE) return true; }
            else { if (Math.Abs(diameter - g_toolCurrent.DiameterIN) > DIAMETER_EQUAL_CLEARANCE) return true; }

            if (Math.Abs(volt - g_toolCurrent.Voltage) > VOLT_EQUAL_CLEARANCE) return true;
            if (Math.Abs(amp - g_toolCurrent.Current) > AMP_EQUAL_CLEARANCE) return true;

            // Compare Image Xscale
            if (g_cropadorner != null)
            {
                Rect rc = g_cropadorner.ClippingRectangle;
                if (Math.Abs(g_toolCurrent.Xbegin * imgTool.ActualWidth - rc.Left) > XSCALE_EQUAL_CLEARANCE) return true;
                if (Math.Abs(g_toolCurrent.Xend * imgTool.ActualWidth - rc.Right) > XSCALE_EQUAL_CLEARANCE) return true;
            }

            return false; // No Different
        }

        private void butQuit_Click(object sender, RoutedEventArgs e)
        {
            if (ToolDifferentFromUI())
            {
                MessageBoxResult res = MessageBox.Show("Do you want to save Changes ? Y or N", "SAVE TOOL CHANGE", MessageBoxButton.YesNoCancel);
                if (res == MessageBoxResult.Cancel) return;
                else if (res == MessageBoxResult.Yes) UpdateToolFromUI();
            }
            // No or Already UpdateToolFromUI()
            this.Close();
        }

        private void butNext_Click(object sender, RoutedEventArgs e)
        {
            if (ToolDifferentFromUI())
            {
                MessageBoxResult res = MessageBox.Show("Do you want to save Changes ? Y or N", "SAVE TOOL CHANGE", MessageBoxButton.YesNoCancel);
                if (res == MessageBoxResult.Cancel) return;
                else if (res == MessageBoxResult.Yes) UpdateToolFromUI();
            }

            // No or Already UpdateToolFromUI()
            if (++index == g_toolList.Count) index = 0;
            g_toolCurrent = g_toolList[index];
            UpdateToolToUI();
        }

        private void butPrev_Click(object sender, RoutedEventArgs e)
        {
            if (ToolDifferentFromUI())
            {
                MessageBoxResult res = MessageBox.Show("Do you want to save Changes ? Y or N", "SAVE TOOL CHANGE", MessageBoxButton.YesNoCancel);
                if (res == MessageBoxResult.Cancel) return;
                else if (res == MessageBoxResult.Yes) UpdateToolFromUI();
            }
            // No or Already UpdateToolFromUI()
            if (--index < 0) index += g_toolList.Count; // = g_toolList.Count-1
            g_toolCurrent = g_toolList[index];
            UpdateToolToUI();
        }

        private void butSave_Click(object sender, RoutedEventArgs e)
        {
            if (ToolDifferentFromUI())
            {
                UpdateToolFromUI();
            }
        }

        private void butCancel_Click(object sender, RoutedEventArgs e)
        {
            UpdateToolToUI();
        }


        /// <summary>
        /// Get Double from String contain single Double Number. Can have Non-Numeric number inside no problem.
        /// </summary>
        /// <param name="singleNum"></param>
        /// <returns></returns>
        public static double GetDoubleFromString(string singleNum)
        {
            Match match = Regex.Match(singleNum, @"(-?)([0-9]+)([,\.]?)([0-9]*)");
            if (!match.Success) throw new InvalidOperationException("Not a string Number");

            if (match.Groups[3].Value != string.Empty)
            {   // Co dau thap phan
                string txt = match.ToString().Replace(match.Groups[3].Value, System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);
                return Convert.ToDouble(txt);
            }
            else return Convert.ToDouble(match.Groups[0].Value); BitmapImage img = new BitmapImage();
        }

        #region Length
        private void txtLength_LostFocus(object sender, RoutedEventArgs e)
        {
            if (txtLength.Text == string.Empty) UpdateLength();
            try { length = GetDoubleFromString(txtLength.Text); UpdateLength(); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Please check NUMBER INPUT again !"); UpdateLength(); }
        }

        private void lbLength_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            SwapLength();
        }

        /// <summary>
        /// Swap UI base on current length
        /// </summary>
        /// <param name="unit"></param>
        private void SwapLength(string unit = null)
        {   // SWAP
            // Neu dang CM
            //     ghi length vao R, doi length sang ft roi ghi vao L, swap text
            // Neu dang FT
            //     ghi length vao R, doi length sang cm roi ghi vao L, swap text
            if (lbLength.Content == CM2FT)
            {   // Dang o CM
                txtLength2.Text = length.ToString("0.###");
                length /= CMperFT;
                txtLength.Text = length.ToString("0.###");
                lbLength.Content = FT2CM;
            }
            else
            {   // Dang o FT
                txtLength2.Text = length.ToString("0.###");
                length *= CMperFT;
                txtLength.Text = length.ToString("0.###");
                lbLength.Content = CM2FT;
            }
        }

        /// <summary>
        /// Update base length (current unit) to UI
        /// </summary>
        private void UpdateLength()
        {   // Update UI Length
            // dang CM : L nhap vao length, R nhap vao length da doi sang FT
            if (lbLength.Content == CM2FT)
            {   // Current is CM
                txtLength.Text = length.ToString("0.###");
                txtLength2.Text = (length / CMperFT).ToString("0.###");
            }
            else
            {   // Current is FT
                txtLength.Text = length.ToString("0.###");
                txtLength2.Text = (length * CMperFT).ToString("0.###");
            }
        }
        #endregion

        #region Weight
        private void txtWeight_LostFocus(object sender, RoutedEventArgs e)
        {
            if (txtWeight.Text == string.Empty) UpdateWeight();
            try { weight = GetDoubleFromString(txtWeight.Text); UpdateWeight(); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Please check NUMBER INPUT again !"); UpdateWeight(); }
        }

        private void lbWeight_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            SwapWeight();
        }

        /// <summary>
        /// Swap UI base on current Weight
        /// </summary>
        /// <param name="unit"></param>
        private void SwapWeight(string unit = null)
        {
            if (lbWeight.Content == KG2LBS)
            {   // Dang o KG
                txtWeight2.Text = weight.ToString("0.###");
                weight *= LBperKG;
                txtWeight.Text = weight.ToString("0.###");
                lbWeight.Content = LBS2KG;
            }
            else
            {   // Dang o LBS
                txtWeight2.Text = weight.ToString("0.###");
                weight /= LBperKG;
                txtWeight.Text = weight.ToString("0.###");
                lbWeight.Content = KG2LBS;
            }
        }

        /// <summary>
        /// Update base Weight (current unit) to UI
        /// </summary>
        private void UpdateWeight()
        {
            if (lbWeight.Content == KG2LBS)
            {   // Current is KG
                txtWeight.Text = weight.ToString("0.###");
                txtWeight2.Text = (weight * LBperKG).ToString("0.###");
            }
            else
            {   // Current is LBS
                txtWeight.Text = weight.ToString("0.###");
                txtWeight2.Text = (weight / LBperKG).ToString("0.###");
            }
        }
        #endregion

        #region Diameter
        private void txtDiameter_LostFocus(object sender, RoutedEventArgs e)
        {
            if (txtDiameter.Text == string.Empty) UpdateDiameter();
            try { diameter = GetDoubleFromString(txtDiameter.Text); UpdateDiameter(); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Please check NUMBER INPUT again !"); UpdateDiameter(); }
        }

        private void lbDiameter_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            SwapDiameter();
        }

        /// <summary>
        /// Swap UI base on current Diameter
        /// </summary>
        /// <param name="unit"></param>
        private void SwapDiameter(string unit = null)
        {
            if (lbDiameter.Content == CM2IN)
            {
                txtDiameter2.Text = diameter.ToString("0.###");
                diameter /= CMperIN;
                txtDiameter.Text = diameter.ToString("0.###");
                lbDiameter.Content = IN2CM;
            }
            else
            {
                txtDiameter2.Text = diameter.ToString("0.###");
                diameter *= CMperIN;
                txtDiameter.Text = diameter.ToString("0.###");
                lbDiameter.Content = CM2IN;
            }
        }

        /// <summary>
        /// Update base Diameter (current unit) to UI
        /// </summary>
        private void UpdateDiameter()
        {
            if (lbDiameter.Content == CM2IN)
            {
                txtDiameter.Text = diameter.ToString("0.###");
                txtDiameter2.Text = (diameter / CMperIN).ToString("0.###");
            }
            else
            {
                txtDiameter.Text = diameter.ToString("0.###");
                txtDiameter2.Text = (diameter * CMperIN).ToString("0.###");
            }
        }
        #endregion

        #region Voltage
        private void txtVoltage_LostFocus(object sender, RoutedEventArgs e)
        {
            if (txtVoltage.Text == string.Empty) UpdateVoltage();
            try { volt = GetDoubleFromString(txtVoltage.Text); UpdateVoltage(); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Please check NUMBER INPUT again !"); UpdateVoltage(); }
        }

        private void UpdateVoltage()
        {
            txtVoltage.Text = volt.ToString("0.###");
        }
        #endregion

        #region Current
        private void txtCurrent_LostFocus(object sender, RoutedEventArgs e)
        {
            if (txtCurrent.Text == string.Empty) UpdateCurrent();
            try { amp = GetDoubleFromString(txtCurrent.Text); UpdateCurrent(); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Please check NUMBER INPUT again !"); UpdateCurrent(); }
        }

        private void UpdateCurrent()
        {
            txtCurrent.Text = amp.ToString("0.###");
        }
        #endregion
    }
}
